package com.dandelion.manager.system.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.dandelion.commons.SystemHelper;
import com.dandelion.commons.constants.ConstantSystemValues;
import com.dandelion.commons.log.LoginLogHelper;
import com.dandelion.commons.spring.SpringContextHolder;
import com.dandelion.commons.utils.Md5Utils;
import com.dandelion.commons.utils.PropertyUtils;
import com.dandelion.commons.utils.StringUtils;
import com.dandelion.commons.utils.mail.MailUtil;
import com.dandelion.commons.utils.mail.MailVo;
import com.dandelion.commons.web.model.AjaxResponse;
import com.dandelion.commons.web.model.DataGrid;
import com.dandelion.manager.system.domain.Auth;
import com.dandelion.manager.system.domain.User;
import com.dandelion.manager.system.repository.UserMapper;
import com.dandelion.manager.system.service.AuthService;
import com.dandelion.manager.system.service.UserService;
import com.dandelion.shiro.ShiroPasswordHelper;


@Service
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserMapper mapper;
	
	@Autowired
	private AuthService authService;

	@Override
	public AjaxResponse add(User record) {
		
		if(this.mapper.existsFileldValue(null, "username", record.getUsername()) > 0) 
			return AjaxResponse.failMsg("该账号已存在！") ;
		if(this.mapper.existsFileldValue(null, "email", record.getEmail()) > 0) 
			return AjaxResponse.failMsg("该邮箱地址已存在！") ;
			
		
		if(!StringUtils.isNotBlank(record.getPassword())) {
			record.setPassword(ConstantSystemValues.INIT_PASS); //系统默认密码
		}
		record.SecureRandomSalt();
		record.setPassword(ShiroPasswordHelper.encodePassword(record.getPassword(), record.getUsername(), record.getUserSalt()));
		this.mapper.insertSelective(record);
		
		return SpringContextHolder.getBean(AjaxResponse.class).reset() ;
	}

	@Override
	public AjaxResponse delete(long id) {
		this.mapper.deleteByPrimaryKey(id);
		return SpringContextHolder.getBean(AjaxResponse.class).reset() ;
	}

	@Override
	public AjaxResponse deleteBatch(Long[] ids) {
		this.mapper.deleteByPrimaryKeys(ids);
		return SpringContextHolder.getBean(AjaxResponse.class).reset() ;
	}

	@Override
	public AjaxResponse update(User record) {
		if(this.mapper.existsFileldValue(record.getId(), "username", record.getUsername()) > 0) 
			return AjaxResponse.failMsg("该账号已存在！") ;
		if(this.mapper.existsFileldValue(record.getId(), "email", record.getEmail()) > 0) 
			return AjaxResponse.failMsg("该邮箱地址已存在！") ;
		
		this.mapper.updateByPrimaryKeySelective(record);
		return SpringContextHolder.getBean(AjaxResponse.class).reset() ;
	}
	
	@Override
	public User get(long id) {
		return this.mapper.selectByPrimaryKey(id);
	}

	@Override
	public List<User> getAllList(User record) {
		return this.mapper.selectAllList(record);
	}

	@Override
	public DataGrid<User> datagrid(User conditions) {
		DataGrid<User> dg = new DataGrid<User>();
		
		Map<String, Object> conditionsMap = new HashMap<String, Object>();
		conditionsMap.put("record", conditions);
		conditionsMap.put("offset", dg.getOffset());
		conditionsMap.put("rows", dg.getPageSize());

		List<User> list = this.mapper.selectAllListPage(conditionsMap);
		int count = this.mapper.getCount(conditionsMap);
		dg.setRows(list);
		dg.setTotal(count);

		return dg;
	}

	public boolean matches(User user, String newPassword) {
		return user.getPassword().equals(encryptPassword(user.getUsername(), newPassword, user.getUserSalt()));
	}

	private String encryptPassword(String username, String password, String salt) {
		return Md5Utils.hash(username + password + salt);
	}

	@Override
	public AjaxResponse login(String username, String password, String captcha) {
		AjaxResponse ajaxResponse = SpringContextHolder.getBean(AjaxResponse.class).reset() ;
		ajaxResponse.setStatus(false);
		
		if(!StringUtils.isNotBlank(username) && !StringUtils.isNotBlank(password)) {
			ajaxResponse.setMessage("用户名或密码不能为空,请重新输入！");
		} else {
			Subject subject = SecurityUtils.getSubject();

			UsernamePasswordToken token = new UsernamePasswordToken(username, password) ;
			try {
				subject.login(token);
				User user = getLoginUser(username, password) ;
				SystemHelper.setCurrentUser(user); // 保存当前用户登录信息
				
				//修改用户登录信息
				User record = new User() ;
				record.setId(user.getId());
				record.setLastLoginTime(new Date()); //修改用户最后登录时间
				record.setLastLoginIp(SystemHelper.getCurrentUserIp());	//修改用户最后登录IP
				this.update(record);
				
				ajaxResponse.setStatus(true);
				ajaxResponse.setMessage("登录成功");
				
				SystemHelper.removeSessionRetryCount();
				
			} catch (UnknownAccountException uae) {
				ajaxResponse.setMessage("该用户名不存在！");
			} catch (ExcessiveAttemptsException eae) {
				ajaxResponse.setMessage("密码错误次数太多，请确认用户名和密码！");
				HashMap<String, Object> map = new HashMap<String, Object>() ;
				map.put("showCaptchaCode", true);
				map.put("resetCaptcha", SystemHelper.getSessionRetryCount().get()%3==0?true:false);//刷新验证码
				ajaxResponse.setObj(map);//需要输入验证码
			} catch (IncorrectCredentialsException ice) {
				ajaxResponse.setMessage("认证信息不正确");
			} catch (LockedAccountException lae) {
				ajaxResponse.setMessage("密码错误次数太多，账号已锁定，请稍后再试！");
			} catch (DisabledAccountException lae) {
				ajaxResponse.setMessage("账号已锁定，请联系管理员！");
			} catch (AuthenticationException ae) {
				ajaxResponse.setMessage("认证失败，请重新登录！");
			}
		}
		
		LoginLogHelper.log(ajaxResponse, username);
		return ajaxResponse ;
	}
	
	@Override
	public void logout() {
		SystemHelper.logout();
	}
	
	@Override
	public User getLoginUser(String username, String password) {
		User user = this.findByUsername(username) ;
		if(null != user) {
			return this.mapper.getLoginUser(username, ShiroPasswordHelper.encodePassword(password, username, user.getUserSalt())) ;
		} else {
			return null ;
		}
	}

	@Override
	public AjaxResponse iForgotPassword(String username) {
		AjaxResponse ajaxResponse = SpringContextHolder.getBean(AjaxResponse.class).reset();

		User user = this.mapper.findByUsername(username) ;
		if (null != user) {
			if(null == user.getEmail() || "".equals(user.getEmail().trim())) {
				ajaxResponse.setStatus(false);
				ajaxResponse.setMessage("你好，你在管理系统中登记的邮箱地址为空，请联系管理员");
			}
			if(!MailUtil.checkMail(user.getEmail().trim())) {
				ajaxResponse.setStatus(false);
				ajaxResponse.setMessage("你好，你在管理系统中登记的邮箱地址格式不正确，请联系管理员");
			}
			
			
			generatorRestPassLink(user);
			ajaxResponse.setMessage("邮件已发到你在该系统注册的邮件中，请按邮件提示重设密码。["+user.getEmail()+"]");
		} else {
			ajaxResponse.setStatus(false);
			ajaxResponse.setMessage("账号不存在！");
		}
		return ajaxResponse;
	}

	private boolean generatorRestPassLink(User user) {
		String secretKey = UUID.randomUUID().toString(); // 密钥
		Date outDate = new Date(System.currentTimeMillis() + 30 * 60 * 1000);// 30分钟后过期
		long date = outDate.getTime()/ 1000 * 1000 ;
		String key = user.getUsername() + "$" + date + "$" + secretKey;
		String digitalSignature = Md5Utils.hash(key);// 数字签名

		User record = new User();
		record.setId(user.getId());
		record.setValidateCode(secretKey);
		record.setOutDatetime(new Date(date));
		this.mapper.updateByPrimaryKeySelective(record);
		
		HttpServletRequest request = SpringContextHolder.getHttpRequest();
		String path = request.getContextPath();
		String basePath = request.getScheme() + "://" + request.getServerName()
				+ ":" + request.getServerPort() + path + "/";
		
		String resetPassHref = basePath + "manager/system/user/checkLink?sid=" + digitalSignature
				+ "&username=" + user.getUsername();
		String emailContent = "请勿回复本邮件.点击下面的链接,重设密码<br/><a href="
                + resetPassHref + " target='_BLANK'>" + resetPassHref
                + "</a>  或者    <a href=" + resetPassHref
                + " target='_BLANK'>点击我重新设置密码</a>"
                + "<br/>tips:本邮件超过30分钟,链接将会失效，需要重新申请'找回密码'" + key
                + "\t" + digitalSignature;
		try {
			MailVo mv = new MailVo() ;
			mv.setSubject(PropertyUtils.getValue("main.iforgot.subject"));
			mv.setRecipientTO(user.getEmail());
			mv.setContent(emailContent);
			
			MailUtil.send(mv) ;
			
		} catch (Exception e) {
			e.printStackTrace();
		}

		return true;
	}

	@Override
	public AjaxResponse checkResetLink(String sid, String username) {
		AjaxResponse ajaxResponse = SpringContextHolder.getBean(AjaxResponse.class).reset();
		if (null != sid && sid.equals("") || null != username && username.equals("")) {
			ajaxResponse.setStatus(false);
			ajaxResponse.setMessage("链接不完整,请重新生成！");
			return ajaxResponse;
		}

		User user = this.mapper.findByUsername(username) ;
		if (null != user) {
			long outDateTime = (user.getOutDatetime().getTime()/1000*1000) ;

			// 表示已经过期
			if (outDateTime <= System.currentTimeMillis()) {
				ajaxResponse.setStatus(false);
				ajaxResponse.setMessage("链接已经过期,请重新申请找回密码！");
				return ajaxResponse;
			}

			String key = user.getUsername() + "$" + outDateTime + "$" + user.getValidateCode();
			String digitalSignature = Md5Utils.hash(key);// 数字签名
			
			//链接验证通过 转到修改密码页面
			if(digitalSignature.equals(sid)) {
				ajaxResponse.setObj(user);
				return ajaxResponse;
			} else {
				ajaxResponse.setStatus(false);
				ajaxResponse.setMessage("链接不正确，是否已经过期了或已修改过?");
				return ajaxResponse;
			}

		} else {
			ajaxResponse.setStatus(false);
			ajaxResponse.setMessage("链接错误,无法找到匹配用户,请重新申请找回密码.");
			return ajaxResponse;
		}
	}

	@Override
	public AjaxResponse changePasswordByForgot(User dto) {
		AjaxResponse ajaxResponse = SpringContextHolder.getBean(AjaxResponse.class).reset() ;
		
		if(null != dto && null != dto.getPassword() && !"".equals(dto.getPassword().trim())) {
			String password = dto.getPassword() ;
			dto.setPassword(null);
			User user = this.mapper.selectOne(dto) ;
			if(null != user) {
				User record = new User() ;
				record.setId(user.getId());
				
				record.SecureRandomSalt();
				record.setPassword(ShiroPasswordHelper.encodePassword(password, dto.getUsername(), record.getUserSalt()));
				
				record.setValidateCode("-");//标示该密码已修改过，如需再修改，需重设申请找回密码
				
				this.mapper.updateByPrimaryKeySelective(record) ;
				
				ajaxResponse.setMessage("重设密码成功，返回登录页面");
				return ajaxResponse;
			} else {
				ajaxResponse.setStatus(false);
				ajaxResponse.setMessage("密码已修改过，如需再修改，需重设申请找回密码.");
				return ajaxResponse;
			}
		} else {
			ajaxResponse.setStatus(false);
			ajaxResponse.setMessage("密码重设失败，原因：数据不完整");
			return ajaxResponse;
		}
	}
	
	@Override
	public AjaxResponse changePassword(User dto) {
		AjaxResponse ajaxResponse = SpringContextHolder.getBean(AjaxResponse.class).reset() ;
		
		dto.setPassword(ShiroPasswordHelper.encodePassword(dto.getOldPassword(), dto.getUsername(), dto.getUserSalt()));
		User checkPassword = this.mapper.selectOne(dto) ; //查询条件 id,username,usersalt,password
		if(null == checkPassword) {
			ajaxResponse.setStatus(false);
			ajaxResponse.setMessage("旧密码不正确！");
			return ajaxResponse ;
		} else {
			User record = new User() ;
			record.setId(dto.getId());
			
			record.SecureRandomSalt();
			record.setPassword(ShiroPasswordHelper.encodePassword(dto.getNewPassword(), dto.getUsername(), record.getUserSalt()));
			this.mapper.updateByPrimaryKeySelective(record) ;
			
			ajaxResponse.setMessage("密码修改成功，请重新登录！");
			return ajaxResponse;
		}
		
	}

	@Override
	public User findByUsername(String username) {
		return this.mapper.findByUsername(username);
	}

	@Override
	public void setUserAccountStatus(Long[] ids, Integer lockedStatus) {
		this.mapper.setUserAccountStatus(ids, lockedStatus);
	}

	@Override
	public void setUserEditableStatus(Long[] ids, Integer editableStatus) {
		this.mapper.setUserEditableStatus(ids, editableStatus);
	}

	@Override
	public void addRoleToUser(Long[] ids, String roleIds) {
		//先删除用户目前所拥有角色
		this.authService.deleteAuthByUserId(ids);
		
		for(int i=0;i<ids.length;i++) {
			Auth record = new Auth() ;
			record.setUser_id(ids[i]);
			record.setRole_ids(roleIds);
			this.authService.add(record); 
		}
	}

}
